#include "include/t3sdk.h"

#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <vector>

using namespace std;
using namespace t3sdk;

static constexpr char s_PushServiceName[] = "hsbroker.mc";
static constexpr char s_OptionTradePushTopicName[] = "secu.opt.busin_push";
static constexpr char s_OptionOrderPushTopicName[] = "secu.opt.entrust_update_notice";
static constexpr int s_SubTimeout = 5000;
static constexpr char s_SubUser[] = "HDServer";
constexpr int s_MaxConnCount = 2;

class CTopicCallback : public IT3SubCallback
{
public:
    unsigned long QueryInterface(const char *iid, IBase **ppv) { return 0U; }
    unsigned long AddRef() { return 0U; }
    unsigned long Release() { return 0U; }

    void OnReceived(IT3Subscriber *lpSub, int subscribeIndex, const void *lpData, int nLength, LPSUBSCRIBE_RECVDATA lpRecvData)
    {
    }

    void OnRecvTickMsg(IT3Subscriber *lpSub, int subscribeIndex, const char *TickMsgInfo)
    {
    }
};

int g_OrderSubflag = 0;
int g_TradeSubFlag = 0;

IT3Subscriber *SubscribePush(IT3Connection *conn, IT3Subscriber *reuser)
{
    char subUserName[128]{};
    snprintf(subUserName, sizeof(subUserName), "%s_Test", s_SubUser);
    CTopicCallback *pushCb = new CTopicCallback;

    IT3Subscriber *subscriber = nullptr;
    if (reuser != nullptr)
    {
        subscriber = reuser;
    }
    else
    {
        subscriber = conn->NewSubscriberEx(pushCb, subUserName, s_SubTimeout, s_PushServiceName);
        if (subscriber == nullptr)
        {
            printf("NewSubscriberEx failed..[%s]\n", conn->GetMCLastError());
            return subscriber;
        }
        subscriber->AddRef();
    }

    IT3SubParam * subParam = NewSubscribeParam();
    if (subParam == nullptr)
    {
        return subscriber;
    }

    subParam->AddRef();
    subParam->ResetFilter();
    subParam->SetFromNow(false); //不需要补缺

    subParam->SetTopicName(s_OptionTradePushTopicName);

    g_TradeSubFlag = subscriber->SubscribeTopic(subParam, (unsigned int)s_SubTimeout);
    if (g_TradeSubFlag <= 0)
    {
        printf("SubscribeTopic %s failed. %s\n", s_OptionTradePushTopicName, conn->GetMCLastError());
        return subscriber;
    }

    subParam->SetTopicName(s_OptionOrderPushTopicName);

    g_OrderSubflag = subscriber->SubscribeTopic(subParam, (unsigned int)s_SubTimeout);
    if (g_OrderSubflag <= 0)
    {
        printf("SubscribeTopic %s failed. %s\n", s_OptionOrderPushTopicName, conn->GetMCLastError());
        return subscriber;
    }

    subParam->Release();
    return subscriber;
}

void CloseConn(IT3Connection *conn, IT3Subscriber *subscribe)
{
    if (subscribe != nullptr)
    {
        int ret = subscribe->CancelSubscribeTopic(g_TradeSubFlag);
        if (ret != 0)
        {
            printf("CancelSubscribeTopic(%d) failed (%s)\n", g_TradeSubFlag, conn->GetErrorMsg(ret));
        }
        else
        {
            printf("CancelSubscribeTopic(%d) success\n", g_TradeSubFlag);
        }
        ret = subscribe->CancelSubscribeTopic(g_OrderSubflag);
        if (ret != 0)
        {
            printf("CancelSubscribeTopic(%d) failed (%s)\n", g_OrderSubflag, conn->GetErrorMsg(ret));
        }
        else
        {
            printf("CancelSubscribeTopic(%d) success\n", g_OrderSubflag);
        }
    }

    subscribe->Release();
    conn->Release();
}

class MyCallback : public IT3Callback
{
public:
    /* IBase接口 */
    unsigned long FUNCTION_CALL_MODE QueryInterface(const char *iid, IBase **ppv) { return 0; }
    unsigned long FUNCTION_CALL_MODE AddRef() { return 0; }
    unsigned long FUNCTION_CALL_MODE Release() { return 0; }

    /* IT3Callback接口函数，具体参考T3sdk开发包 */

    void FUNCTION_CALL_MODE OnConnect(IT3Connection *lpConnection)
    {
    }

    void FUNCTION_CALL_MODE OnSafeConnect(IT3Connection *lpConnection)
    {
    }

    void FUNCTION_CALL_MODE OnRegister(IT3Connection *lpConnection)
    {
        printf("[OnRegister] conn=%p\n", lpConnection);
    }

    void FUNCTION_CALL_MODE OnClose(IT3Connection *lpConnection)
    {
    }

    void FUNCTION_CALL_MODE OnReceiveNotify(IT3Connection *lpConnection, int hSend, const void *lpData, int nLength)
    {
        printf("[OnReceiveNotify] conn=%p\n", lpConnection);
    }

    void FUNCTION_CALL_MODE OnReceived(IT3Connection *lpConnection, int hSend, const void *lpData, int nLength)
    {
    }
};

std::vector<IT3Connection *> InitConnection(IT3Config *lpConfig, MyCallback *lpcallback)
{
    std::vector<IT3Connection *> conns;
    /* 建立多个连接 */
    for (int i = 0; i < s_MaxConnCount; i++)
    {
        IT3Connection *conn = NewConnection(lpConfig); //申请连接对象
        conn->AddRef();

        if (conn->Create(lpcallback)) //创建连接对象
        {
            printf("Create connection error!\n");
            return conns;
        }

        int ret = conn->Connect(5000);
        if (ret != 0) // timeout为0，建立异步连接 连接上时 OnRegister通知
        {
            printf("connect error! %s\n", conn->GetErrorMsg(ret));
            return conns;
        }

        conns.push_back(conn);
    }

    ::sleep(2); //等待连接注册成功

    return conns;
}

IT3Config *Configer()
{
    IT3Config *lpConfig = NewConfig(); // 配置对象指针，可以定义多个（多个连接使用不同配置文件时）
    lpConfig->AddRef();

    if (lpConfig->Load("./t3sdk.ini") != 0)
    {
        printf("load config failed..\n");
    }

    return lpConfig;
}

int main()
{
    T3sdkInit();

    IT3Config *configer = Configer();

    MyCallback callback;
    std::vector<IT3Connection *> lpConnections = InitConnection(configer, &callback);
    if (lpConnections.empty())
    {
        printf("connect failed..\n");
        return 0;
    }

    assert(lpConnections.size() >= 2);

    IT3Connection *conn1 = lpConnections[0];
    IT3Connection *conn2 = lpConnections[1];

    //第一个连接先订阅
    auto subscriber = SubscribePush(conn1, nullptr);
    ::sleep(2);

    printf("Begin to close...\n");
    //释放第一个连接
    CloseConn(conn1, subscriber);

    printf("Begin to subscribe second...\n");
    //用第二个连接订阅
    SubscribePush(conn2, nullptr);

    getchar();

    T3sdkUnInit();
    return 0;
}